package com.toolkit.util;
import com.toolkit.enumData.ExceptionCodeMsg;
import com.toolkit.exceptionHandler.BusinessException;
import com.toolkit.module.calculation.dto.PrepaymentLoanDto;
import com.toolkit.module.calculation.vo.CommercialLoanVo;
import com.toolkit.module.calculation.vo.PrepaymentLoanVo;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;

public class EarlyRepaymentCalculatorUtil {

    private double loanAmount; // 贷款总额
    private double annualInterestRate; // 年利率
    private int loanTermMonths; // 贷款期限（月）
    private LocalDate firstPaymentDate; // 首次还贷时间
    private LocalDate earlyRepaymentDate; // 提前还贷时间
    private double partialRepaymentAmount; // 部分还款金额

    private Integer faType;  //调整方案 0缩短还款期限 1减少还款金额
    private Integer repaymentType; //贷款方式 0.部分还款 2.一次还清v

    public EarlyRepaymentCalculatorUtil(PrepaymentLoanDto prepaymentLoanDto) {
        this.loanAmount = prepaymentLoanDto.getLoanTotalAmount().doubleValue()*10000;
        this.annualInterestRate = prepaymentLoanDto.getLoanLendingRate().doubleValue() / 100;
        this.loanTermMonths = prepaymentLoanDto.getLoanTerm();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate localDateFrist = LocalDate.parse(prepaymentLoanDto.getFristRepaymentDate(), formatter);
        LocalDate localDateRepay = LocalDate.parse(prepaymentLoanDto.getAdvanceRepaymentDate(), formatter);
        this.firstPaymentDate = localDateFrist;
        this.earlyRepaymentDate = localDateRepay;
        this.partialRepaymentAmount = prepaymentLoanDto.getRepaymentAmount().doubleValue()*10000;
        this.faType = prepaymentLoanDto.getFaType();
        this.repaymentType = prepaymentLoanDto.getRepaymentType();
    }

    public PrepaymentLoanVo calculateEarlyRepayment() {
        PrepaymentLoanVo prepaymentLoanVo = new PrepaymentLoanVo();
        List<Payment> payments = generatePayments(); // 生成所有月度还款计划
        long monthsPassed = ChronoUnit.MONTHS.between(firstPaymentDate, earlyRepaymentDate); // 计算从首次还款到提前还款的时间（月）
        if(monthsPassed>=loanTermMonths){
            throw new BusinessException(ExceptionCodeMsg.REPAY_DATE_ERROR);
        }
        double remainingPrincipal = loanAmount; // 剩余本金初始化为贷款总额

        double principalPaidBeforeEarlyRepayment = 0; // 初始化提前还款前已支付本金
        double interestPaidBeforeEarlyRepayment = 0; // 初始化提前还款前已支付利息

        for (int i = 0; i < monthsPassed && i < payments.size(); i++) { // 处理已经完成的还款
            Payment payment = payments.get(i); // 获取当月还款信息
            remainingPrincipal -= payment.principal; // 减去已还本金
            principalPaidBeforeEarlyRepayment += payment.principal; // 累加已支付本金
            interestPaidBeforeEarlyRepayment += payment.interest; // 累加已支付利息
        }

        if (repaymentType ==0) { // 如果有部分还款
            remainingPrincipal -= partialRepaymentAmount; // 减去部分还款金额
//            principalPaidBeforeEarlyRepayment += partialRepaymentAmount; // 累加部分还款金额
        }else if (repaymentType == 1) { // 如果有提前还款
            prepaymentLoanVo.setRepaidTerm((int)monthsPassed);
            prepaymentLoanVo.setRemainTerm(loanTermMonths - (int) monthsPassed);
            prepaymentLoanVo.setRepaidPrincipal(Math.round(principalPaidBeforeEarlyRepayment*100.0)/100.0);
            prepaymentLoanVo.setRemainPrincipal(Math.round(remainingPrincipal*100.0)/100.0);
            prepaymentLoanVo.setRepaidInterest(Math.round(interestPaidBeforeEarlyRepayment*100.0)/100.0);
            prepaymentLoanVo.setRemainInterest(Math.round((calculateTotalInterest(payments)-interestPaidBeforeEarlyRepayment)*100.0)/100.0);
            prepaymentLoanVo.setAdvanceType(repaymentType);
            prepaymentLoanVo.setAdvanceRepayPrincipal(Math.round(remainingPrincipal*100.0)/100.0);
            prepaymentLoanVo.setAdvanceRemainPrincipal(0.0);
            prepaymentLoanVo.setAdvanceSchemeType(faType);
            prepaymentLoanVo.setAdvanceRemainTerm(0);
            prepaymentLoanVo.setReduceTerm(prepaymentLoanVo.getRemainTerm() );
            prepaymentLoanVo.setSaveInterest(Math.round(prepaymentLoanVo.getRemainInterest()*100.0)/100.0);
            CommercialLoanVo orgLoanVo=new CommercialLoanVo();
            orgLoanVo.setTotalAmount(Math.round((calculateTotalInterest(payments)+loanAmount)*100.0)/100.0);
            orgLoanVo.setPrincipalRate(Math.round(annualInterestRate*100*100.0)/100.0);
            orgLoanVo.setLoanTerm(loanTermMonths);
            orgLoanVo.setTotalPrincipalAmount(Math.round(loanAmount*100.0)/100.0);
            orgLoanVo.setTotalInterestAmount(Math.round(calculateTotalInterest(payments)*100.0)/100.0);
            orgLoanVo.setFirstRePayment(Math.round((payments.get(0).totalPayment)*100.0)/100.0);
            orgLoanVo.setType(2);
            prepaymentLoanVo.setOriginalLoanInfo(orgLoanVo);
            return prepaymentLoanVo;
        }

        if (remainingPrincipal <= 0) { // 如果剩余本金小于等于0
            prepaymentLoanVo.setRepaidTerm((int)monthsPassed);
            prepaymentLoanVo.setRemainTerm(loanTermMonths - (int) monthsPassed);

            prepaymentLoanVo.setRepaidPrincipal(Math.round(principalPaidBeforeEarlyRepayment*100.0)/100.0);
            prepaymentLoanVo.setRemainPrincipal(Math.round(remainingPrincipal*100.0)/100.0);
            prepaymentLoanVo.setRepaidInterest(Math.round(interestPaidBeforeEarlyRepayment*100.0)/100.0);
            prepaymentLoanVo.setRemainInterest(Math.round((calculateTotalInterest(payments)-interestPaidBeforeEarlyRepayment)*100.0)/100.0);

            prepaymentLoanVo.setAdvanceType(repaymentType);
            prepaymentLoanVo.setAdvanceRepayPrincipal(Math.round(remainingPrincipal*100.0)/100.0);
            prepaymentLoanVo.setAdvanceRemainPrincipal(0.0);
            prepaymentLoanVo.setAdvanceSchemeType(faType);
            prepaymentLoanVo.setAdvanceRemainTerm(0);
            prepaymentLoanVo.setReduceTerm(prepaymentLoanVo.getRemainTerm() );
            prepaymentLoanVo.setSaveInterest(Math.round(prepaymentLoanVo.getRemainInterest()*100.0)/100.0);
            CommercialLoanVo orgLoanVo=new CommercialLoanVo();
            orgLoanVo.setTotalAmount(Math.round((calculateTotalInterest(payments)+loanAmount)*100.0)/100.0);
            orgLoanVo.setPrincipalRate(Math.round(annualInterestRate*100*100.0)/100.0);
            orgLoanVo.setLoanTerm(loanTermMonths);
            orgLoanVo.setTotalPrincipalAmount(Math.round(loanAmount*100.0)/100.0);
            orgLoanVo.setTotalInterestAmount(Math.round(calculateTotalInterest(payments)*100.0)/100.0);
            orgLoanVo.setFirstRePayment(Math.round((payments.get(0).totalPayment)*100.0)/100.0);
            orgLoanVo.setType(2);
            prepaymentLoanVo.setOriginalLoanInfo(orgLoanVo);
            return prepaymentLoanVo;
        }

        double monthlyInterestRate = annualInterestRate / 12; // 计算月利率
        double monthlyPrincipal = loanAmount / loanTermMonths; // 每月固定偿还的本金

        List<Payment> newPayments = null;
        double totalPaidInterestAfterEarlyRepayment;
        double totalPaidPrincipalAfterEarlyRepayment;
        double totalInterestSaved;

        if (faType==0) {
            // 情况一：月还款额不变，缩短还款期限
            double firstMonthPayment = payments.get(Integer.parseInt(monthsPassed+"")).principal; // 使用原来的首月还款额
            newPayments = generateNewPaymentsUnchangedMonthlyPayment(remainingPrincipal, firstMonthPayment, monthlyInterestRate);
        } else if(faType==1){
            // 情况二：减少月还款额，还款期限不变
            newPayments = generateNewPaymentsReducedMonthlyPayment(remainingPrincipal, loanTermMonths - (int) monthsPassed);
        }

        totalPaidInterestAfterEarlyRepayment = 0; // 初始化提前还款后总支付利息
        totalPaidPrincipalAfterEarlyRepayment = 0; // 初始化提前还款后总支付本金
        for (Payment payment : newPayments) { // 计算新还款计划中的总利息和本金
            totalPaidInterestAfterEarlyRepayment += payment.interest; // 累加利息
            totalPaidPrincipalAfterEarlyRepayment += payment.principal; // 累加本金
        }
        double lix2= interestPaidBeforeEarlyRepayment+totalPaidInterestAfterEarlyRepayment;
        double lix1= calculateTotalInterest(payments);
        totalInterestSaved=lix1-lix2;

        prepaymentLoanVo.setRepaidTerm((int)monthsPassed);
        prepaymentLoanVo.setRemainTerm(loanTermMonths - (int) monthsPassed);

        prepaymentLoanVo.setRepaidPrincipal(Math.round(principalPaidBeforeEarlyRepayment*100.0)/100.0);
        prepaymentLoanVo.setRemainPrincipal(Math.round((remainingPrincipal+partialRepaymentAmount)*100.0)/100.0);
        prepaymentLoanVo.setRepaidInterest(Math.round(interestPaidBeforeEarlyRepayment*100.0)/100.0);
        prepaymentLoanVo.setRemainInterest(Math.round((calculateTotalInterest(payments)-interestPaidBeforeEarlyRepayment)*100.0)/100.0);

        prepaymentLoanVo.setAdvanceType(repaymentType);
        prepaymentLoanVo.setAdvanceRepayPrincipal(Math.round(partialRepaymentAmount*100.0)/100.0);
        prepaymentLoanVo.setAdvanceRemainPrincipal(Math.round(remainingPrincipal*100.0)/100.0);

        prepaymentLoanVo.setAdvanceSchemeType(faType);
        prepaymentLoanVo.setAdvanceRemainTerm(newPayments.size());
        prepaymentLoanVo.setReduceTerm(prepaymentLoanVo.getRemainTerm() - newPayments.size());
        prepaymentLoanVo.setSaveInterest(Math.round(totalInterestSaved*100.0)/100.0);
        CommercialLoanVo orgLoanVo=new CommercialLoanVo();
        orgLoanVo.setTotalAmount(Math.round((calculateTotalInterest(payments)+loanAmount)*100.0)/100.0);
        orgLoanVo.setPrincipalRate(Math.round(annualInterestRate*100*100.0)/100.0);
        orgLoanVo.setLoanTerm(loanTermMonths);
        orgLoanVo.setTotalPrincipalAmount(Math.round(loanAmount*100.0)/100.0);
        orgLoanVo.setTotalInterestAmount(Math.round(calculateTotalInterest(payments)*100.0)/100.0);
        orgLoanVo.setFirstRePayment(Math.round(payments.get(0).totalPayment*100.0)/100.0);
        orgLoanVo.setLastRePayment(Math.round(payments.get(payments.size()-1).totalPayment*100.0)/100.0);
        if(payments.size()>1) {
            orgLoanVo.setDecreasingInterest(Math.round((payments.get(0).totalPayment - payments.get(1).totalPayment) * 100.0) / 100.0);
        }
        orgLoanVo.setType(2);
        prepaymentLoanVo.setOriginalLoanInfo(orgLoanVo);
        CommercialLoanVo newLoanVo=new CommercialLoanVo();
        newLoanVo.setTotalAmount(Math.round((totalPaidInterestAfterEarlyRepayment+remainingPrincipal)*100.0)/100.0);
        newLoanVo.setPrincipalRate(Math.round(annualInterestRate*100*100.0)/100.0);
        newLoanVo.setLoanTerm(newPayments.size());
        newLoanVo.setTotalPrincipalAmount(Math.round(remainingPrincipal*100.0)/100.0);
        newLoanVo.setTotalInterestAmount(Math.round(totalPaidInterestAfterEarlyRepayment*100.0)/100.0);
        newLoanVo.setFirstRePayment(Math.round(newPayments.get(0).totalPayment*100.0)/100.0);
        newLoanVo.setLastRePayment(Math.round(newPayments.get(newPayments.size()-1).totalPayment*100.0)/100.0);
        newLoanVo.setType(2);
        if(newPayments.size()>1) {
            newLoanVo.setDecreasingInterest(Math.round((newPayments.get(0).totalPayment - newPayments.get(1).totalPayment) * 100.0) / 100.0);
        }
        prepaymentLoanVo.setNewLoanInfo(newLoanVo);
        return prepaymentLoanVo;

    }

    private List<Payment> generatePayments() {
        List<Payment> payments = new ArrayList<>(); // 创建存储还款计划的列表
        double monthlyInterestRate = annualInterestRate / 12; // 计算月利率
        double monthlyPrincipal = loanAmount / loanTermMonths; // 每月固定偿还的本金

        double remainingPrincipal = loanAmount; // 初始化剩余本金为贷款总额
        for (int month = 0; month < loanTermMonths; month++) { // 循环生成每个月的还款信息
            double interest = remainingPrincipal * monthlyInterestRate; // 计算当月利息
            double principal=0;
            if( month == loanTermMonths-1) {
                principal = remainingPrincipal; // 最后一个月还清剩余本金
            }else{
                principal = monthlyPrincipal; // 每月固定的本金
                remainingPrincipal -= principal; // 更新剩余本金
            }
            payments.add(new Payment(principal + interest, principal, interest)); // 将当月还款信息添加到列表中
        }
        return payments; // 返回完整的还款计划
    }

    private List<Payment> generateNewPaymentsUnchangedMonthlyPayment(double remainingPrincipal, double monthlyPayment, double monthlyInterestRate) {
        List<Payment> payments = new ArrayList<>(); // 创建存储新的还款计划的列表

        while (remainingPrincipal > 0) {
            double interest = Math.min(remainingPrincipal * monthlyInterestRate, remainingPrincipal); // 计算当月利息，不超过剩余本金
            double principal = Math.min(monthlyPayment , remainingPrincipal); // 计算当月本金，不超过剩余本金
            remainingPrincipal -= principal; // 更新剩余本金
            payments.add(new Payment(principal + interest, principal, interest)); // 将当月还款信息添加到列表中
        }
        return payments; // 返回新的还款计划
    }

    private List<Payment> generateNewPaymentsReducedMonthlyPayment(double remainingPrincipal, int remainingMonths) {
        List<Payment> payments = new ArrayList<>(); // 创建存储新的还款计划的列表
        double monthlyInterestRate = annualInterestRate / 12; // 计算月利率
        double monthlyPrincipal = remainingPrincipal / remainingMonths; // 计算月利率
        for (int month = 0; month < remainingMonths; month++) { // 循环生成每个月的新还款信息
            double interest = Math.min(remainingPrincipal * monthlyInterestRate, remainingPrincipal); // 计算当月利息，不超过剩余本金
            double principal = Math.min(monthlyPrincipal, remainingPrincipal); // 计算当月本金，不超过剩余本金
            remainingPrincipal -= principal; // 更新剩余本金
            payments.add(new Payment(principal + interest, principal, interest)); // 将当月还款信息添加到列表中

            if (remainingPrincipal <= 0) break; // 如果剩余本金小于等于0，结束循环
        }
        return payments; // 返回新的还款计划
    }

    private double calculateTotalInterest(List<Payment> payments) {
        double totalInterest = 0; // 初始化总利息
        for (Payment payment : payments) { // 循环累加每个还款周期的利息
            totalInterest += payment.interest; // 累加利息
        }
        return totalInterest; // 返回总利息
    }

    private static class Payment {
        double totalPayment; // 总还款额
        double principal; // 还本金
        double interest; // 还利息

        Payment(double totalPayment, double principal, double interest) {
            this.totalPayment = totalPayment; // 设置总还款额
            this.principal = principal; // 设置还本金
            this.interest = interest; // 设置还利息
        }
    }
}








